<?php
// +----------------------------------------------------------------------
// | 通用服务层
// +----------------------------------------------------------------------
// | 主要服务于API接口基类
// +----------------------------------------------------------------------
// | 错误编码头 202xxx
// +----------------------------------------------------------------------
namespace common\service;

use common\exception\LogicException;
use support\ErrorCode;
use think\module\Module;

class CommonService
{
    // model对象
    /** @var \think\Model\RelationModel */
    protected $modelObject;

    // 当前模块编码
    protected $currentModuleCode;

    // 当前模块关联结构
    protected $currentModuleLinkRelation = [];

    // 哪些模块外键为link_id的
    protected $moduleForeignKeyIsLinkId = ['follow', 'timelog'];

    /**
     * @var \common\service\mediaService
     */
    protected $mediaService;

    /**
     * @var object 对象实例
     */
    protected static $instance = [];

    public function __construct($class = null)
    {
        // 实例化当前model
        if (class_exists($class)) {
            $this->modelObject = new $class();
        } else {
            throw_strack_exception("Class does not exist.", ErrorCode::CLASS_DOES_NOT_EXIST);
        }
        $this->mediaService = new MediaService();
    }

    /**
     * 初始化
     * @param string $moduleName
     * @return \common\service\CommonService
     */
    public static function instance($moduleName = '')
    {
        $class = '\\common\\model\\' . string_initial_letter($moduleName) . 'Model';
        if (!empty($moduleName)) {
            if (empty(self::$instance[$class]) && !isset(self::$instance[$class])) {
                self::$instance[$class] = new static($class);
            }
        } else {
            throw_strack_exception("Module does not exist.", ErrorCode::MODULE_NOT_EXIST);
        }

        return self::$instance[$class];
    }

    /**
     * 处理创建自定义字段
     * @param $data
     * @param string $moduleCode
     * @return array
     */
    public function dealCreateCustomField($data, $moduleCode = "")
    {
        $dealData = [];
        $currentModuleCode = $moduleCode === "" ? \request()->getModuleCode() : $moduleCode;
        foreach ($data as $field => $value) {
            if (array_key_exists($field, Module::$moduleDictData['field_index_by_code'][$currentModuleCode]['fixed'])) {
                // 固定字段
                $dealData[$field] = $value;
            } else {
                // 自定义字段
                if (array_key_exists($field, Module::$moduleDictData['field_index_by_code'][$currentModuleCode]['custom'])) {
                    if (array_key_exists('json', $dealData)) {
                        $dealData['json'][$field] = $value;
                    } else {
                        $dealData['json'] = [$field => $value];
                    }
                }
            }
        }
        return $dealData;
    }

    /**
     * 查询一条基础方法
     * @param $param
     * @param string $moduleCode
     * @return mixed
     */
    public function find($param, $moduleCode = '')
    {
        if (!empty($moduleCode)) {
            $this->modelObject->setCurrentModuleCode($moduleCode);
        }
        $resData = $this->modelObject->findData($param);
        return $resData;
    }

    /**
     * 查询多条基础方法
     * @param $param
     * @param string $moduleCode
     * @return mixed
     */
    public function select($param, $moduleCode = '')
    {
        if (!empty($moduleCode)) {
            $this->modelObject->setCurrentModuleCode($moduleCode);
        }
        $resData = $this->modelObject->selectData($param);
        return $resData;
    }

    /**
     * 创建基础方法
     * @param $data
     * @return array|bool
     * @throws \think\Exception
     */
    public function create($data)
    {
        // 处理自定义字段
        $dealData = $this->dealCreateCustomField($data);

        // 执行新增操作
        $resData = $this->modelObject->addItem($dealData);
        if (!$resData) {
            // 通用创建失败错误码 001
            throw_strack_exception($this->modelObject->getError(), ErrorCode::COMMON_CREATE_FAILURE);
        } else {
            // 返回成功数据
            return $resData;
        }
    }

    /**
     * 更新基础方法
     * @param $data
     * @param bool $throwNoDataChangeException
     * @return array|bool
     * @throws \Exception
     */
    public function update($data, $throwNoDataChangeException = true)
    {
        // 处理自定义字段
        $dealData = $this->dealUpdateCustomField($data);

        $resData = $this->modelObject->modifyItem($dealData);
        if (!$resData) {
            if ($throwNoDataChangeException === false && $this->modelObject->getErrorCode() === \think\exception\ErrorCode::NO_DATA_HAS_BEEN_CHANGED) {
                return $resData;
            }
            // 通用修改失败错误码 002
            throw_strack_exception($this->modelObject->getError(), ErrorCode::COMMON_UPDATE_FAILURE);
        } else {
            // 返回成功数据
            return $resData;
        }
    }

    /**
     * 删除基础方法
     * @param $param
     * @return array
     */
    public function delete($param)
    {
        // 获取当前模块删除主键
        $masterIdData = $this->modelObject->field('id')->where($param["param"]['filter'])->select();

        if (!empty($masterIdData)) {
            $masterIds = join(',', array_column($masterIdData, 'id'));
            $resData = $this->modelObject->deleteItem([
                'id' => ["IN", $masterIds]
            ]);

            if (!$resData) {
                // 通用删除失败错误码 003
                throw_strack_exception($this->modelObject->getError(), ErrorCode::COMMON_DELETE_FAILURE);
            } else {
                // 返回成功数据
                return ['ids' => $masterIds];
            }

        } else {
            throw_strack_exception('Delete item does not exist.', ErrorCode::DELETE_ITEM_NOT_EXIST);
        }
    }

    /**
     * 生成批量新增关联外键
     * @param $data
     * @param $preModule
     * @param $param
     * @return mixed
     */
    protected function generateRecursiveDataForeignKey(&$data, $preModule, $param)
    {
        if (in_array($preModule, $this->moduleForeignKeyIsLinkId)) {
            $data['link_id'] = $param['id'];
            $data['module_id'] = $param['module_data']['id'];
        } else {
            if ($param['module_data']['type'] === 'entity') {
                $data['entity_id'] = $param['id'];
                $data['entity_module_id'] = $param['module_data']['id'];
                $data['module_id'] = $param['module_data']['id'];

                //生成初始集 优先使用 initial_episode_code
                if (!empty($param['initial_episode_code'])) {
                    $data['initial_episode_code'] = $param['initial_episode_code'];
                } elseif (!empty($param['code'])) {
                    $data['initial_episode_code'] = $param['code'];
                } else {
                    $data['initial_episode_code'] = "";
                }

            } else {
                if (
                    array_key_exists($preModule, $this->currentModuleLinkRelation)
                    && !empty($this->currentModuleLinkRelation[$preModule])
                    && $this->currentModuleLinkRelation[$preModule]['type'] === 'horizontal'
                ) {
                    // 判断是否为自定义字段
                    if (!empty($data['json'])) {
                        $data['json'][$this->currentModuleLinkRelation[$preModule]['link_id']] = $param['id'];
                    } else {
                        $data['json'] = [
                            $this->currentModuleLinkRelation[$preModule]['link_id'] => $param['id']
                        ];
                    }

                } else {
                    $data[$param['module_data']['code'] . '_id'] = $param['id'];
                }
            }
        }

        return $data;
    }

    /**
     * 生成批量新增新增数据
     * @param $data
     * @param $module
     * @return array
     * @throws \Exception
     */
    protected function generateRecursiveDataAddData($data, $module)
    {
        $addData = [];

        if ($module === 'media') {
            // 媒体信息需要单独处理
            $this->mediaService->generateMediaData($data);
        }

        // 当前模块配置
        $currentModuleParam = Module::$moduleDictData['module_index_by_code'][$module];
        if ($currentModuleParam['type'] = 'entity') {
            $data['module_id'] = $currentModuleParam['id'];
        }

        foreach ($data as $filed => $value) {
            if (array_key_exists($filed, Module::$moduleDictData['field_index_by_code'][$module]['fixed'])) {
                $addData[$filed] = $value;
            } else if (array_key_exists($filed, Module::$moduleDictData['field_index_by_code'][$module]['custom'])) {
                if (array_key_exists('json', $addData)) {
                    $addData['json'][$filed] = $value;
                } else {
                    $addData['json'] = [
                        $filed => $value
                    ];
                }
            }
        }

        return $addData;
    }

    /**
     * 保存递归处理数据
     * @param $newModelObject
     * @param $addData
     * @return mixed
     * @throws \Exception
     */
    public function saveRecursiveData($newModelObject, $addData)
    {
        $addResult = $newModelObject->addItem($addData);

        if (!$addResult) {
            // 添加失败错误码 001
            $errorCode = !empty($newModelObject->getErrorCode()) ? $newModelObject->getErrorCode() : -202012;
            if ($errorCode === -411111) {
                // 已经存在数据
                return $newModelObject->getCheckUniqueExitData();
            } else {
                throw new LogicException($newModelObject->getError(), $errorCode);
            }
        } else {
            // 返回成功数据
            return $addResult;
        }
    }

    /**
     * 递归处理多级新增项
     * @param $resData
     * @param $item
     * @param $module
     * @param $newModelObject
     * @param $currentModuleData
     * @return array
     * @throws \Exception
     */
    protected function recursiveDataItemCreate(&$resData, $item, $module, $newModelObject, $currentModuleData)
    {
        $addData = $this->generateRecursiveDataAddData($item['data'], $module);

        $addResult = $this->saveRecursiveData($newModelObject, $addData);

        // 新增当前模块数据
        $currentModuleCode = \request()->getModuleCode();
        if ($module === $currentModuleCode) {
            $resData[] = $addResult['id'];
        }

        // 传入下个循环值
        $nextParam = [
            'module_data' => $currentModuleData,
            'id' => $addResult['id']
        ];

        //当前模块是集 那么存入当前插入数据的code
        if ($module === "episode") {
            $nextParam['code'] = $addResult['code'] ?? "";
        }
        //继承 父级的 initial_episode_code属性
        $nextParam['initial_episode_code'] = $addResult['initial_episode_code'] ?? "";

        if (array_key_exists('after', $item)) {
            $this->recursiveDataCreate($resData, $item['after'], $nextParam);
        }

        return $resData;
    }

    /**
     * 递归处理多级新增
     * @param $resData
     * @param $data
     * @param array $param
     * @return mixed
     * @throws \Exception
     */
    protected function recursiveDataCreate(&$resData, $data, $param = [])
    {
        // 把上一级外键批量填入下一层
        if (!empty($param)) {
            foreach ($data as $preModule => &$preItem) {
                if (!empty($preItem[0])) {
                    foreach ($preItem as &$preAddItem) {
                        if (!empty($preAddItem['data'])) {
                            $this->generateRecursiveDataForeignKey($preAddItem['data'], $param['module_data']['pre_module'], $param);
                        }
                    }
                } else {
                    if (!empty($preItem['data'])) {
                        $this->generateRecursiveDataForeignKey($preItem['data'], $param['module_data']['pre_module'], $param);
                    }
                }
            }
        }

        // 深度遍历
        foreach ($data as $module => $item) {

            // 所属模块对象名称
            $currentModuleCode = '';
            $realModuleCode = $module;
            if (array_key_exists($module, Module::$moduleDictData['module_index_by_code'])) {
                $currentModuleCode = $module;
            } else if (array_key_exists($module, $this->currentModuleLinkRelation)) {
                $currentModuleCode = $this->currentModuleLinkRelation[$module]['module_code'];
            }

            if (!empty($currentModuleCode)) {
                $currentModuleData = Module::$moduleDictData['module_index_by_code'][$currentModuleCode];
                $currentModuleData['pre_module'] = $realModuleCode;


                if (Module::$moduleDictData['module_index_by_code'][$currentModuleCode]['type'] === 'entity') {
                    $modelObjectName = "EntityModel";
                } else {
                    $modelNamePre = Module::$moduleDictData['module_index_by_code'][$currentModuleCode]['name'];
                    $modelObjectName = "{$modelNamePre}Model";
                }

                $modelObjectClass = '\\common\\model\\' . $modelObjectName;
                $newModelObject = new $modelObjectClass();

                if (!empty($item[0])) {
                    // 多维数组

                    // 循环添加
                    foreach ($item as $addItem) {
                        // 不存在data跳入下个循环
                        if (!array_key_exists('data', $addItem)) {
                            return $resData;
                        }

                        $this->recursiveDataItemCreate($resData, $addItem, $currentModuleCode, $newModelObject, $currentModuleData);
                    }
                } else {
                    $this->recursiveDataItemCreate($resData, $item, $currentModuleCode, $newModelObject, $currentModuleData);
                }
            }
        }
    }

    /**
     * 关联创建数据
     * @param $data
     * @param $moduleCode
     * @return array
     */
    public function createRelationData($data, $moduleCode)
    {
        // 批量新增数据
        $resData = [];

        $class = get_module_model_name(Module::$moduleDictData['module_index_by_code'][$moduleCode]);
        $objectModel = new $class();

        $objectModel->setCurrentModuleCode($moduleCode);
        $objectModel->startTrans();//强制开启事务

        $this->currentModuleLinkRelation = $objectModel->parserFilterModuleRelation(true);

        try {
            $this->recursiveDataCreate($resData, $data);
            $objectModel->commit();
            return $resData;
        } catch (\Exception $e) {
            $objectModel->rollback();
            throw_strack_exception($e->getMessage(), $e->getCode());
        }
    }

    /**
     * 通用新增处理函数，如果有多层数据逐步添加，单层数据直接添加
     * @param $param
     * @param $moduleData
     * @return array|bool
     * @throws \think\Exception
     */
    public function commonCreate($param, $moduleData)
    {
        if (check_is_relation_create($param["data"])) {
            // 多维数组走关联新增方法
            return $this->createRelationData($param["data"], $param['module_code']);
        } else {
            // 一维数组走普通新增方法

            if ($moduleData['type'] === 'entity') {
                // 实体类型增加当前模块id
                $param['data']['module_id'] = $moduleData['id'];
            }

            return $this->create($param["data"]);
        }
    }

    /**
     * 多维数组走关联新增方法 TODO
     * @param $data
     * @return array
     */
    protected function updateRelationData($data)
    {
        return [];
    }


    /**
     * 通用更新处理函数，如果有多层数据逐步更新，单层数据直接更新
     * @param $param
     * @param bool $throwNoDataChangeException
     * @return array|bool
     * @throws \Exception
     */
    public function commonUpdate($param, $throwNoDataChangeException = true)
    {
        if (check_is_relation_create($param["data"])) {
            // 多维数组走关联新增方法
            return $this->updateRelationData($param["data"]);
        } else {
            // 一维数组走普通新增方法
            return $this->update($param["data"], $throwNoDataChangeException);
        }
    }

    /**
     * 处理更新自定义字段
     * @param $data
     * @param string $currentModuleCode
     * @return array
     */
    public function dealUpdateCustomField($data, $currentModuleCode = "")
    {
        $pk = $this->modelObject->getPk();

        if (!isset($data[$pk])) {
            throw_strack_exception("missing pk in data", ErrorCode::MISS_PK_IN_DATA);
        }

        $oldData = $this->modelObject->find($data[$pk]);

        if (empty($oldData)) {
            throw_strack_exception("can`t found match record", ErrorCode::NO_MATCH_DATA);
        }

        $dealData = [];
        $currentModuleCode = $currentModuleCode === "" ? \request()->getModuleCode() : $currentModuleCode;
        foreach ($data as $field => $value) {
            if (array_key_exists($field, Module::$moduleDictData['field_index_by_code'][$currentModuleCode]['fixed'])) {
                // 固定字段
                $dealData[$field] = $value;
            } else {
                // 自定义字段
                if (array_key_exists($field, Module::$moduleDictData['field_index_by_code'][$currentModuleCode]['custom'])) {
                    if (array_key_exists('json', $dealData)) {
                        $dealData['json'][$field] = $value;
                    } else {
                        $dealData['json'] = [$field => $value];
                    }
                }
            }
        }

        if (array_key_exists('json', $dealData)) {
            $oldJson = !empty($oldData['json']) ? json_decode($oldData['json'], true) : [];
            $dealData['json'] = array_merge($oldJson, $dealData['json']);
        }

        return $dealData;
    }

    /**
     * 设置当前模块code
     * @param mixed $currentModuleCode
     */
    public function setCurrentModuleCode($currentModuleCode)
    {
        $this->currentModuleCode = $currentModuleCode;
    }

    /**
     * 获得格式化数据
     * @param \think\model\RelationModel $model
     * @param $data
     * @return mixed
     */
    public static function getFormatAttrData($model, &$data)
    {
        foreach ($data as $k => &$v) {
            // 检测属性获取器
            $method = 'get' . $model->parseName($k, 1) . 'Attr';

            if (method_exists($model, $method)) {
                $v = $model->$method($v, $data);
            }
        }
        return $data;
    }
}
